package nc.impl.hrpbm.budgetswap.budgetswapvo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.lang.String;
import java.util.stream.Stream;

import nc.bs.framework.common.NCLocator;
import nc.hrpbm.impl.control.service.BmBudgetControlServiceImpl;
import nc.impl.hrpbm.budgetreply.budgetreply.BudgetSwapWriteBackForPeojectUtil;
import nc.itf.hrpbm.budgetmake.budgetmake.IBudgetMakeVOService;
import nc.vo.hrpbm.budgetmake.AggBudgetMakeVO;
import nc.vo.hrpbm.budgetmake.BudgetMakeDetailVO;
import nc.vo.hrpbm.budgetmake.BudgetMakeVO;
import nc.vo.pub.lang.UFDateTime;
import nc.vo.pub.lang.UFDouble;
import nc.vo.pubapp.pattern.exception.ExceptionUtils;
import nc.vo.pubapp.pattern.pub.SqlBuilder;
import nccloud.impl.hrpbm.pub.common.HrpbmServiceSupport;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import nc.vo.pub.BusinessException;
import nc.vo.pub.SuperVO;
import nc.vo.pub.VOStatus;
import nc.pub.billcode.vo.BillCodeContext;

import nc.vo.hrpbm.budgetswap.BudgetswapVO;
import nc.vo.hrpbm.budgetswap.BudgetswapBVO;
import nc.vo.hrpbm.budgetswap.AggBudgetswapVO;
import nc.itf.hrpbm.budgetswap.budgetswapvo.IBudgetswapVOService;

import nc.vo.pub.pf.BillStatusEnum;
import nc.bs.framework.common.InvocationInfoProxy;

public class  BudgetswapVOServiceImpl extends HrpbmServiceSupport implements IBudgetswapVOService {

	public BudgetswapVOServiceImpl() {
		super("f43d7ec1-1fa0-4ef3-a423-5408a1fc0591");
	}


	@Override
	public AggBudgetswapVO[] listAggBudgetswapVOByPk(String...pks) throws BusinessException{
		return listAggBudgetswapVOByPk(false,pks);
	}

	@Override
	public AggBudgetswapVO[] listAggBudgetswapVOByPk(boolean blazyLoad,String... pks) throws BusinessException{
		return dao.listByPksWithOrder(AggBudgetswapVO.class,pks,blazyLoad);
	}

	@Override
	public AggBudgetswapVO findAggBudgetswapVOByPk(String pk) throws BusinessException{
		return dao.findByPkWithDr(AggBudgetswapVO.class, pk, true);
	}

	@Override
	public  AggBudgetswapVO[] listAggBudgetswapVOByCondition(String condition) throws BusinessException{
		return listAggBudgetswapVOByCondition(condition,new String[]{"pk_budgetswap"});
	}
	@Override
	public  AggBudgetswapVO[] listAggBudgetswapVOByCondition(String condition,String[] orderPath) throws BusinessException{
		return dao.listByCondition(AggBudgetswapVO.class, condition, false,false,orderPath);
	}
	@Override
	public BudgetswapVO[] listBudgetswapVOByPk(String... pks) throws BusinessException{
		return dao.listByPk(BudgetswapVO.class, pks, true);
	}

	@Override
	public  BudgetswapVO findBudgetswapVOByPk(String pk) throws BusinessException{
		return dao.findByPk(BudgetswapVO.class, pk, true);
	}

	@Override
	public  BudgetswapVO[] listBudgetswapVOByCondition(String condition) throws BusinessException{
		return listBudgetswapVOByCondition(condition,new String[]{"pk_budgetswap"});
	}
	@Override
	public  BudgetswapVO[] listBudgetswapVOByCondition(String condition,String[] orderPath) throws BusinessException{
		return dao.listByCondition(BudgetswapVO.class, condition, false,false,orderPath);
	}

	@Override
	public String[] listBudgetswapVOPkByCond(String condition) throws BusinessException{
		return listBudgetswapVOPkByCond(condition,new String[]{"pk_budgetswap"});
	}
	@Override
	public String[] listBudgetswapVOPkByCond(String condition,String[] orderPath) throws BusinessException{
		if(StringUtils.isEmpty(condition)) {
			condition = " 1 = 1 ";
		}
		StringBuilder sql = new StringBuilder();
		sql.append(" select pk_budgetswap from ").append(BudgetswapVO.getDefaultTableName());
		sql.append(" where ").append(condition);
		if (ArrayUtils.isNotEmpty(orderPath)) {
			sql.append(" order by ").append(StringUtils.join(orderPath, ", "));
		}
		return (String[]) dao.getBaseDAO().executeQuery(sql.toString(), (rs) -> {
			List<String> pks = new ArrayList<>();
			while (rs.next()) {
				pks.add(rs.getString(1));
			}
			return pks.toArray(new String[0]);
		});
	}
	@Override
	public void initDefaultData(BudgetswapVO vo){
		if(vo.getAttributeValue("pk_group") == null){
			vo.setAttributeValue("pk_group",InvocationInfoProxy.getInstance().getGroupId());
		}
		if(vo.getAttributeValue("billmaker") == null){
			vo.setAttributeValue("billmaker",InvocationInfoProxy.getInstance().getUserId());
		}
		if(vo.getAttributeValue("billdate") == null){
			vo.setAttributeValue("billdate",new UFDateTime(InvocationInfoProxy.getInstance().getBizDateTime()));
		}
		if(vo.getAttributeValue("billtype") == null){
			vo.setAttributeValue("billtype","BM35");
		}
		if(vo.getAttributeValue("approvestatus") == null){
			vo.setAttributeValue("approvestatus",BillStatusEnum.FREE.toIntValue());
		}
		if(vo.getAttributeValue("creationtime") == null){
			vo.setAttributeValue("creationtime", new UFDateTime());
		}
	}
	@Override
	public AggBudgetswapVO preAddAggBudgetswapVO(AggBudgetswapVO vo, Map<String,Object> userJson) throws BusinessException{

		getMainVO(vo).setStatus(VOStatus.NEW);
		initDefaultData((BudgetswapVO)getMainVO(vo));
		String pk_org = InvocationInfoProxy.getInstance().getGroupId();

		//下面这段要判断是否是树表界面插件
		Map<String,String> data = userJson!=null && userJson.get("data") != null?(Map<String,String>)userJson.get("data"):null;
		if(data!=null && data.size()>0){
			String parentKey = data.get("parentKey");
			String parentPk = data.get("parentPk");
			getMainVO(vo).setAttributeValue(parentKey,parentPk);
		}

		//编码规则生成vo的编码
		BillCodeContext billCodeContext = getBillCodeContext("hrpbmbudgetswap");
		if(billCodeContext == null){
			throw new BusinessException("当前编码规则不存在，请到【编码规则定义-全局】节点检查是否存在"+"hrpbmbudgetswap");
		}
		if(billCodeContext.isPrecode()){
			String pk_group = InvocationInfoProxy.getInstance().getGroupId();
			String code = getBillcodeManage().getPreBillCode_RequiresNew("hrpbmbudgetswap", pk_group, pk_org);
			getMainVO(vo).setAttributeValue("billcode",code);
		}

		return vo;
	}
	@Override
	public AggBudgetswapVO preAddAggBudgetswapVO(Map<String,Object> userJson) throws BusinessException{
		AggBudgetswapVO result = null;

		BudgetswapVO mainvo = new BudgetswapVO();
		//设置默认值
		initDefaultData(mainvo);

		AggBudgetswapVO aggvo = new AggBudgetswapVO();
		aggvo.setParent(mainvo);
		result = aggvo;
		return preAddAggBudgetswapVO(result,userJson);
	}

	@Override
	public AggBudgetswapVO preEditAggBudgetswapVO(String pk) throws BusinessException{
		return dao.findByPkWithDr(AggBudgetswapVO.class, pk, true);
	}
	@Override
	public AggBudgetswapVO copyAggBudgetswapVO(String pk) throws BusinessException{

		AggBudgetswapVO vo = dao.findByPkWithDr(AggBudgetswapVO.class, pk, false);

		if (vo == null) {
			throw new BusinessException("没有查到对应的数据，请重试");
		}

		getMainVO(vo).setPrimaryKey(null);
		getMainVO(vo).setStatus(VOStatus.NEW);

		getMainVO(vo).setAttributeValue("srcbilltype",null);
		getMainVO(vo).setAttributeValue("srcbillid",null);

		getMainVO(vo).setAttributeValue("billcode",null);
		getMainVO(vo).setAttributeValue("name",null);
		String defaultGroup = InvocationInfoProxy.getInstance().getGroupId();
		getMainVO(vo).setAttributeValue("pk_group",defaultGroup);
		String pk_org = InvocationInfoProxy.getInstance().getGroupId();
		if(getMainVO(vo).getAttributeValue("pk_org") != null){
			pk_org = (String)getMainVO(vo).getAttributeValue("pk_org");
		}
		//编码规则生成vo的编码
		BillCodeContext billCodeContext = getBillCodeContext("hrpbmbudgetswap",InvocationInfoProxy.getInstance().getGroupId(),pk_org);
		if(billCodeContext == null){
			throw new BusinessException("当前编码规则不存在，请到【编码规则定义-全局】节点检查是否存在"+"hrpbmbudgetswap");
		}
		if(billCodeContext.isPrecode()){
			String pk_group = InvocationInfoProxy.getInstance().getGroupId();
			String code = getBillcodeManage().getPreBillCode_RequiresNew("hrpbmbudgetswap", pk_group, pk_group);
			getMainVO(vo).setAttributeValue("billcode",code);
		}
		getMainVO(vo).setAttributeValue("approvestatus", BillStatusEnum.FREE.toIntValue());
		getMainVO(vo).setAttributeValue("billmaker", InvocationInfoProxy.getInstance().getUserId());
		getMainVO(vo).setAttributeValue("approver", null);
		getMainVO(vo).setAttributeValue("approvenote", null);
		getMainVO(vo).setAttributeValue("approvedate", null);
		//设置审计信息为空
		getMainVO(vo).setAttributeValue("creator",null);
		getMainVO(vo).setAttributeValue("creationtime",null);
		getMainVO(vo).setAttributeValue("modifier",null);
		getMainVO(vo).setAttributeValue("modifiedtime",null);
		getMainVO(vo).setAttributeValue("billdate", new UFDateTime(InvocationInfoProxy.getInstance().getBizDateTime()));
		getMainVO(vo).setAttributeValue("modifiedtime", null);

		BudgetswapBVO[] budgetswapBVOs = (BudgetswapBVO[])vo.getChildren(BudgetswapBVO.class);
		if(budgetswapBVOs!=null && budgetswapBVOs.length>0){
			Arrays.stream(budgetswapBVOs).forEach(subvo->{
				subvo.setPrimaryKey(null);
				subvo.setStatus(VOStatus.NEW);
				subvo.setAttributeValue("srcbilltype",null);
				subvo.setAttributeValue("srcbillid",null);
				subvo.setAttributeValue("rowno", null);
				subvo.setAttributeValue("srcrowno", null);
			});
		}
		return vo;
	}

	public AggBudgetswapVO[] saveAggBudgetswapVO(AggBudgetswapVO vo ) throws BusinessException{
		return this.saveAggBudgetswapVO(vo, null);
	}

	public AggBudgetswapVO[] saveAggBudgetswapVO(AggBudgetswapVO vo , String nodetype ) throws BusinessException{

		AggBudgetswapVO[] returnValues = null;
		AggBudgetswapVO[] oriVos = null;

		validateBeforeSave(vo);
		lockUtils.lock(vo);
		String pk = getVOPrimaryKey(vo);
		setDefaultVal(vo);

		if(StringUtils.isEmpty(pk)){
			returnValues = insert(vo); //插入
		}else{
			//查询修改前的VO
			oriVos = this.listAggBudgetswapVOByPk(pk);
			returnValues = update(vo); //更新
		}

		//ADD BY WW 2024-05-10 保存时 推送预算（校验余额、冻结）
		pushBudgetForSave(returnValues, oriVos, true);

		return returnValues;

	}

	/**
	 * 保存前处理编码规则
	 * @param vos
	 */
	private void setBillCode(AggBudgetswapVO... vos) throws BusinessException {
		if (ArrayUtils.isEmpty(vos)) {
			return;
		}
		for(AggBudgetswapVO vo : vos) {
			String pk = getVOPrimaryKey(vo);
			if (StringUtils.isNotEmpty(pk)) {
				continue;
			}
			String pk_group = InvocationInfoProxy.getInstance().getGroupId();
			String pk_org = pk_group;
			if (getMainVO(vo).getAttributeValue("pk_org") != null){
				pk_org = (String)getMainVO(vo).getAttributeValue("pk_org");
			}
			BillCodeContext billCodeContext = getBillCodeContext("hrpbmbudgetswap", pk_group, pk_org);
			if (billCodeContext == null) {
				throw new BusinessException("当前编码规则不存在，请到【编码规则定义-全局】节点检查是否存在"+"hrpbmbudgetswap");
			}
			if (billCodeContext.isPrecode()) {
				String code = (String) getMainVO(vo).getAttributeValue("billcode");
				getBillcodeManage().commitPreBillCode("hrpbmbudgetswap", pk_group, pk_org, code);
			} else {
				if(getMainVO(vo).getAttributeValue("billcode") == null){
					String code = getBillcodeManage().getBillCode_RequiresNew("hrpbmbudgetswap", pk_group, pk_org,vo);
					getMainVO(vo).setAttributeValue("billcode",code);
				}
			}
		}
	}
	/**
	 * 保存前设置审计信息
	 * @param vos
	 */
	private void setAuditInfo(AggBudgetswapVO... vos) throws BusinessException {
		if(ArrayUtils.isNotEmpty(vos)) {
			UFDateTime now = new UFDateTime();
			for(AggBudgetswapVO vo : vos) {
				String pk = getVOPrimaryKey(vo);
				if(StringUtils.isEmpty(pk)){
					//设置创建人创建时间
					getMainVO(vo).setAttributeValue("creator",InvocationInfoProxy.getInstance().getUserId());
					getMainVO(vo).setAttributeValue("creationtime",now);
					getMainVO(vo).setAttributeValue("billdate",now);
					getMainVO(vo).setAttributeValue("billmaker", InvocationInfoProxy.getInstance().getUserId()); // 制单人
					getMainVO(vo).setAttributeValue("modifier",null);
					getMainVO(vo).setAttributeValue("modifiedtime",null);
				}else{
					//设置修改人修改时间
					getMainVO(vo).setAttributeValue("modifier",InvocationInfoProxy.getInstance().getUserId());
					getMainVO(vo).setAttributeValue("modifiedtime",now);
					getMainVO(vo).setAttributeValue("modifiedtime",now);
				}
			}
		}
	}
	/**
	 * 保存前处理一些默认值
	 * @param vos
	 */
	private void setDefaultVal(AggBudgetswapVO... vos) throws BusinessException {
		setBillCode(vos);
		setAuditInfo(vos);
		//其他默认值处理
	}

	// 给单表（行编辑表）单独适配
	@Override
	public AggBudgetswapVO[] saveAggBudgetswapVO(AggBudgetswapVO[] vos) throws BusinessException {

		AggBudgetswapVO[] results = this.saveAggBudgetswapVO(vos, null);

		return results;

	}

	public AggBudgetswapVO[] saveAggBudgetswapVO(AggBudgetswapVO[] vos , String nodetype ) throws BusinessException {
		if (ArrayUtils.isEmpty(vos)) {
			return new AggBudgetswapVO[0];
		}
		validateBeforeSave(vos);

		lockUtils.lock(vos);
		setDefaultVal(vos); // 设置默认值
		List<String> pks = Arrays.stream(vos).filter(v -> getMainVO(v).getStatus() == VOStatus.DELETED)
				.map(v -> getMainVO(v).getPrimaryKey()).collect(Collectors.toList()); // 删除单据主键
		if (pks == null || pks.size() == 0) {
			return save(vos, true);
		}
		AggBudgetswapVO[] deleteVOs = dao.listByPk(AggBudgetswapVO.class, pks.toArray(new String[0]));
		if (deleteVOs == null || deleteVOs.length == 0) {
			return save(vos, true);
		}
		for (int i = 0; i < deleteVOs.length; i++) {
			SuperVO mainVO = getMainVO(deleteVOs[i]);
			// 删除单据时校验单据状态
			Integer approveStatus = (Integer) mainVO.getAttributeValue("approvestatus");
			if (approveStatus != null && !approveStatus.equals(-1)) {
				throw new BusinessException("第" + (i + 1) + "张单据处理失败：单据状态不正确，不能删除！");
			}
			// 删除单据时回退单据号
			String code = (String)mainVO.getAttributeValue("billcode");
			if (StringUtils.isNotEmpty(code)) {
				String pk_group = InvocationInfoProxy.getInstance().getGroupId();
				getBillcodeManage().returnBillCodeOnDelete("hrpbmbudgetswap", pk_group, pk_group, code, deleteVOs[i]);
			}
		}
		return save(vos,true);
	}

	/**
	 * 保存前校验
	 * - 表体行转出科室+转出预算号<>转入科室+转入预算号。相同给出提示：转出和转入预算号不能相同。
	 * - 不允许相同预算号既在转出行出现，又在转入行出现。出现给出提示：不允许相同预算号既在转出方又在转入方。
	 * - 调剂金额大于0.
	 * - 调剂金额<=转出原预算余额，如超出则给出提示：调剂金额必须小于等于转出原预算余额。转出存在相同预算号时，调剂金额要合并判断。
	 * 预算年度+预算类型+转出科室存在未审核完成单据时，不允许再新增保存。
	 * @param vos
	 * @throws BusinessException
	 */
	private void validateBeforeSave(AggBudgetswapVO... vos) throws BusinessException {
		if(nccloud.commons.lang.ArrayUtils.isEmpty(vos)) {
			return;
		}

		Set<String> unionKeySet = new HashSet<>(16);

		Set<String> yearSet = new HashSet<>(16);
		Set<String> budgettypeSet = new HashSet<>(16);
		Set<String> budgetdeptSet = new HashSet<>(16);

		for (AggBudgetswapVO aggBudgetswapVO : vos) {
			BudgetswapVO headVO = aggBudgetswapVO.getParentVO();
			// 如果单据不为自由态，则跳过校验
			if(headVO.getApprovestatus() ==null || !headVO.getApprovestatus().equals(BillStatusEnum.FREE.toIntValue())){
				continue;
			}
			yearSet.add(headVO.getNyear());
			budgettypeSet.add(headVO.getPk_budgettype());
			BudgetswapBVO[] bodyVOs = (BudgetswapBVO[])aggBudgetswapVO.getAllChildrenVO();
			if(nccloud.commons.lang.ArrayUtils.isEmpty(bodyVOs)) {
				continue;
			}

			Map<String, UFDouble> budgetnoAmountMap = new HashMap<>(bodyVOs.length);
			Set<String> budgetno_outSet = new HashSet<>(bodyVOs.length);
			Set<String> budgetno_inSet = new HashSet<>(bodyVOs.length);
			for (BudgetswapBVO bodyVO : bodyVOs) {
				String pk_budgetdept_out = bodyVO.getPk_budgetdept_out();
				String budgetno_out = bodyVO.getBudgetno_out();
				String pk_budgetdept_in = bodyVO.getPk_budgetdept_in();
				String budgetno_in = bodyVO.getBudgetno_in();
				if(nccloud.commons.lang.StringUtils.isEmpty(pk_budgetdept_out)) {

					ExceptionUtils.wrappBusinessException("转出科室不能为空！");
				}
				if(nccloud.commons.lang.StringUtils.isEmpty(pk_budgetdept_in)) {
					ExceptionUtils.wrappBusinessException("转入科室不能为空！");
				}
				if(nccloud.commons.lang.StringUtils.isEmpty(budgetno_out)) {
					ExceptionUtils.wrappBusinessException("转出预算号不能为空！");
				}
				if(nccloud.commons.lang.StringUtils.isEmpty(budgetno_in)) {
					ExceptionUtils.wrappBusinessException("转入预算号不能为空！");
				}
				budgetno_outSet.add(budgetno_out);
				budgetno_inSet.add(budgetno_in);
				if((pk_budgetdept_out+budgetno_out).equals((pk_budgetdept_in+budgetno_in))) {
					ExceptionUtils.wrappBusinessException("转出和转入预算号不能相同。");
				}

				UFDouble swap_amount = bodyVO.getSwap_amount();
				if(swap_amount == null || swap_amount.doubleValue() <= 0) {
					ExceptionUtils.wrappBusinessException("调剂金额应大于0。");
				}

				UFDouble currBalanceOut =  budgetnoAmountMap.get(budgetno_out);
				if(currBalanceOut == null) {
					UFDouble balance_out = bodyVO.getBalance_out() == null ? UFDouble.ZERO_DBL : bodyVO.getBalance_out();
					budgetnoAmountMap.put(budgetno_out, balance_out.sub(swap_amount));
				}else{
					budgetnoAmountMap.put(budgetno_out, currBalanceOut.sub(swap_amount));
				}
				// 新增单据校验是否有未审核完成的
				if(VOStatus.NEW == headVO.getStatus()) {
					String unionKey = headVO.getNyear() + headVO.getPk_budgettype() + pk_budgetdept_out;
					unionKeySet.add(unionKey);
				}

				budgetdeptSet.add(pk_budgetdept_out);
			}

			for (String budgetnoOut : budgetno_outSet) {
				if(budgetno_inSet.contains(budgetnoOut)) {
					ExceptionUtils.wrappBusinessException("不允许相同预算号既在转出方又在转入方。");
				}
			}

			for (Map.Entry<String, UFDouble> entry : budgetnoAmountMap.entrySet()) {
				UFDouble calcBalanceOut = entry.getValue();
				if(calcBalanceOut.doubleValue() < 0) {
					ExceptionUtils.wrappBusinessException("累计调剂金额必须小于等于转出原预算余额。");
				}
			}
		}

		if(!unionKeySet.isEmpty()) {
			// 查询已持久化的未审核完成单据
			SqlBuilder whereSql = new SqlBuilder();
			whereSql.append(" dr = 0 and ");
			// 审批状态为： -1自由，2审批中，3提交 （1审批通过，0审批未通过的排除）
			whereSql.append("approvestatus", new Integer[]{-1,2,3});
			whereSql.append(" and ");
			whereSql.append("nyear", yearSet.toArray(new String[0]));
			whereSql.append(" and ");
			whereSql.append("pk_budgettype",budgettypeSet.toArray(new String[0]));
			whereSql.append(" and ");
			whereSql.append(" pk_budgetswap in ( select pk_budgetswap from hrpbm_budgetswap_b where dr = 0 and  ");
			whereSql.append("pk_budgetdept_out", budgetdeptSet.toArray(new String[0]));
			whereSql.append(" ) ");
			AggBudgetswapVO[] existAggVOs = this.listAggBudgetswapVOByCondition(whereSql.toString());
			if(nccloud.commons.lang.ArrayUtils.isNotEmpty(existAggVOs)){
				for (AggBudgetswapVO existAggVO : existAggVOs) {
					BudgetswapVO existHeadVO = existAggVO.getParentVO();
					BudgetswapBVO[] existBodyVOs = (BudgetswapBVO[])existAggVO.getAllChildrenVO();
					if(nccloud.commons.lang.ArrayUtils.isEmpty(existBodyVOs)) {
						continue;
					}
					for (BudgetswapBVO existBodyVO : existBodyVOs) {
						String exist_pk_budgetdept_out = existBodyVO.getPk_budgetdept_out();
						boolean exist = unionKeySet.contains(existHeadVO.getNyear() + existHeadVO.getPk_budgettype() + exist_pk_budgetdept_out);
						if(exist) {
							ExceptionUtils.wrappBusinessException("预算年度+预算类型+转出科室存在未审核完成单据，不允许新增保存。");

						}
					}
				}
			}
		}


	}

	@Override
	public AggBudgetswapVO[] deleteAggBudgetswapVOs(Map<String,String> tsMap) throws BusinessException{
		return deleteAggBudgetswapVOs(tsMap, null);
	}
	@Override
	public AggBudgetswapVO[] deleteAggBudgetswapVOs(Map<String,String> tsMap, String nodetype) throws BusinessException{
		AggBudgetswapVO[] vos = dao.listByPk(AggBudgetswapVO.class,tsMap.keySet().toArray(new String[0]));
		if (vos == null || vos.length == 0) {
			throw new BusinessException("没有查到对应的数据，请重试");
		}

		lockUtils.lock(vos);
		validate(vos,tsMap);

		String pk_group = InvocationInfoProxy.getInstance().getGroupId();
		Arrays.stream(vos).forEach(vo->{
			String code = (String)getMainVO(vo).getAttributeValue("billcode");
			try {
				getBillcodeManage().returnBillCodeOnDelete("hrpbmbudgetswap",pk_group,pk_group,code,vo);
			} catch (BusinessException e) {
				ExceptionUtils.wrappBusinessException(e.getMessage(),e);
			}
		});
		delete(vos,true);

		//ADD BY WW 2024-05-10推送预算控制平台
		this.pushBudgetForDelete(vos);

		return vos;
	}

	//校验  包括ts校验  已提交校验
	private void validate(AggBudgetswapVO[] vos,Map<String,String> tsMap) throws BusinessException{

		Boolean isPass = true;
		String error = "";    //错误信息
		if(ArrayUtils.isEmpty(vos)){
			isPass = false;
		}

		for(int i = 0 ; i < vos.length ; i++){
			SuperVO mainvo = vos[i].getParentVO();
			UFDateTime ts = (UFDateTime)mainvo.getAttributeValue("ts");
			if(!StringUtils.equals(tsMap.get(mainvo.getPrimaryKey()),ts.toString())){
				isPass = false;
				break;
			}
			Integer approvestatus = (Integer) mainvo.getAttributeValue("approvestatus");
			if(approvestatus == null || approvestatus != BillStatusEnum.FREE.toIntValue()){
				error += "第"+(i+1)+"张单据处理失败：审批状态不正确，不能删除！\n";
			}
		}
		if(!isPass) {
			throw new BusinessException("您操作的数据已被他人修改或删除，请刷新后重试！");
		}
		if(!"".equals(error)){
			throw new BusinessException(error);
		}
	}

	@Override
	public <T> T[] loadTreeData(Class<T> clazz,Map<String,Object> userJson) throws BusinessException{
		String condition = "dr = 0 ";
		if(userJson.get("pk_transtype")!=null){
			String pk_transtype = (String)userJson.get("pk_transtype");
			if(!StringUtils.isEmpty(pk_transtype)){
				condition += " and transtypepk='"+pk_transtype+"'";
			}
		}

		return dao.listByCondition(clazz, condition, false);
	}

	@Override
	public String[] queryChildPksByParentId(Class childClazz, String parentId) throws BusinessException{
		String cond = " pk_budgetswap = '" + parentId + "' ";
		SuperVO[] vos  = (SuperVO[]) dao.listByCondition(childClazz, cond, false);
		if (vos == null || vos.length == 0) {
			return new String[0];
		}
		return Stream.of(vos).map(vo -> vo.getPrimaryKey()).toArray(String[]::new);
	}


	public SuperVO[] queryChildVOByPks(Class childClazz, String[] pks) throws BusinessException{
		return (SuperVO[]) dao.listByPk(childClazz, pks, false);
	}

	/**
	 * 提交前校验:
	 * 检查单据状态
	 * @throws BusinessException
	 * */
	private void validateCommitAggBudgetswapVO(AggBudgetswapVO... vos) throws BusinessException {
		if(ArrayUtils.isEmpty(vos)) {
			return ;
		}
		List<AggBudgetswapVO> list = Arrays.stream(vos)
				.filter(item ->item.getParentVO()!=null)
				.filter(item->{
					Integer status =  (Integer) item.getParentVO().getAttributeValue("approvestatus");
					return status==null||status!=BillStatusEnum.FREE.toIntValue()&&status!=BillStatusEnum.NOPASS.toIntValue();
				}).map(item->item)
				.collect(Collectors.toList());
		if(list == null||list.size() == 0) {
			return;
		}
		String errors = "";
		for(AggBudgetswapVO vo  : list) {
			errors+="单据号：["+vo.getParentVO().getAttributeValue("billcode")+"]提交失败，失败原因：单据状态不正确，请检查。\n";
		}
		throw new BusinessException(errors);
	}
	/**
	 * 收回前校验:
	 * 检查单据状态
	 * @throws BusinessException
	 * */
	private void validateUnCommitAggBudgetswapVO(AggBudgetswapVO... vos) throws BusinessException {
		if(ArrayUtils.isEmpty(vos)) {
			return ;
		}
		List<AggBudgetswapVO> list = Arrays.stream(vos)
				.filter(item ->item.getParentVO()!=null)
				.filter(item->{
					Integer status =  (Integer)item.getParentVO().getAttributeValue("approvestatus");
					return status==null||status==BillStatusEnum.FREE.toIntValue();
				}).map(item->item)
				.collect(Collectors.toList());
		if(list == null||list.size() == 0) {
			return;
		}
		String errors = "";
		for(AggBudgetswapVO vo  : list) {
			errors+="单据号：["+vo.getParentVO().getAttributeValue("billcode")+"]收回失败，失败原因：单据状态不正确，请检查。\n";
		}
		throw new BusinessException(errors);
	}
	@Override
	public Object commitAggBudgetswapVO(String actionName,Map<String,String> tsMap,Object assign) throws BusinessException{
		AggBudgetswapVO[] vos = dao.listByPk(AggBudgetswapVO.class,getAllPks(tsMap),false);
		validateTs(tsMap,vos);
		//提交前校验及业务逻辑
		validateCommitAggBudgetswapVO(vos);
		Map<String,Object> res = this.execFlows(actionName,"BM35",assign,vos);
		//提交后业务逻辑
		// modify by wy at 2024年11月26日 预算调剂回写采购系统四级项目
//		BudgetSwapWriteBackForPeojectUtil.ApproveWriteBackForProjectUtil(vos);
//		int c = 1 / 0;
		return res;
	}

	@Override
	public Object batchCommitAggBudgetswapVO(String actionName,Map<String,String> tsMap) throws BusinessException{
		AggBudgetswapVO[] vos = dao.listByPk(AggBudgetswapVO.class,getAllPks(tsMap),false);
		validateTs(tsMap,vos);
		//批量提交前校验及业务逻辑
		validateCommitAggBudgetswapVO(vos);
		Map<String,Object> res = this.execFlows(actionName,"BM35",vos);
		//批量提交后业务逻辑
		// modify by wy at 2024年11月26日 预算调剂回写采购系统四级项目
//		BudgetSwapWriteBackForPeojectUtil.ApproveWriteBackForProjectUtil(vos);
//		int c = 1 / 0;
		return res;
	}

	@Override
	public Object uncommitAggBudgetswapVO(String actionName,Map<String,String> tsMap) throws BusinessException{
		AggBudgetswapVO[] vos = dao.listByPk(AggBudgetswapVO.class,getAllPks(tsMap),false);
		validateTs(tsMap,vos);
		//收回前校验及业务逻辑
		validateUnCommitAggBudgetswapVO(vos);
		Map<String,Object> res = this.execFlows(actionName,"BM35",vos);
		//收回后业务逻辑
		return res;
	}

	@Override
	public Object batchUncommitAggBudgetswapVO(String actionName,Map<String,String> tsMap) throws BusinessException{
		AggBudgetswapVO[] vos = dao.listByPk(AggBudgetswapVO.class,getAllPks(tsMap),false);
		validateTs(tsMap,vos);
		//批量收回前校验及业务逻辑
		validateUnCommitAggBudgetswapVO(vos);
		Map<String,Object> res = this.execFlows(actionName,"BM35",vos);
		//批量收回后业务逻辑
		return res;
	}


	@Override
	public AggBudgetswapVO[] callbackSAVE(AggBudgetswapVO...vos) throws BusinessException{
		if(ArrayUtils.isEmpty(vos)) {
			return null;
		}
		//同步单据状态和审批状态(只有提交时候需要手动设置审批状态。其余审批完后审批状态都已更新)
		Arrays.stream(vos).forEach(v->{
			v.getParent().setAttributeValue("approvestatus",BillStatusEnum.COMMIT.toIntValue());
		});
		vos = this.saveAggBudgetswapVO(vos);
		return vos;

	}


	@Override
	public AggBudgetswapVO[] callbackUNSAVE(AggBudgetswapVO...vos) throws BusinessException{
		if(ArrayUtils.isEmpty(vos)) {
			return null;
		}
		vos = this.saveAggBudgetswapVO(vos);
		return vos;

	}


	@Override
	public AggBudgetswapVO[] callbackAPPROVE(AggBudgetswapVO...vos) throws BusinessException{
		if(ArrayUtils.isEmpty(vos)) {
			return null;
		}
		// 转出-减少方
		Map<String, UFDouble> budgetDecreaseMap = new HashMap<>();
		// 转入-增加方
		Map<String, UFDouble> budgetIncreaseMap = new HashMap<>();

		for (AggBudgetswapVO aggBudgetswapVO : vos) {
			BudgetswapVO headVO = aggBudgetswapVO.getParentVO();
			Integer approvestatus = headVO.getApprovestatus();
			if(approvestatus == null || !approvestatus.equals(BillStatusEnum.APPROVED.toIntValue())){
				continue;
			}
			BudgetswapBVO[] bodyVOs = aggBudgetswapVO.getChildrenVOs();
			if(ArrayUtils.isEmpty(bodyVOs)) {
				continue;
			}
			for (BudgetswapBVO bodyVO : bodyVOs) {
				// 获取预算号和调整金额
				String budgetno_out = bodyVO.getBudgetno_out_ref();
				UFDouble outAmount = budgetDecreaseMap.get(budgetno_out);
				if(outAmount == null){
					budgetDecreaseMap.put(budgetno_out, bodyVO.getSwap_amount());
				}else {
					budgetDecreaseMap.put(budgetno_out, outAmount.add(bodyVO.getSwap_amount()));
				}

				String budgetno_in = bodyVO.getBudgetno_in_ref();
				UFDouble inAmount = budgetIncreaseMap.get(budgetno_in);
				if(inAmount == null){
					budgetIncreaseMap.put(budgetno_in, bodyVO.getSwap_amount());
				}else {
					budgetIncreaseMap.put(budgetno_in, inAmount.add(bodyVO.getSwap_amount()));
				}
			}

		}
		if(!budgetDecreaseMap.isEmpty() || !budgetIncreaseMap.isEmpty()){
			Set<String> budgetNoSet = new HashSet<>();
			budgetNoSet.addAll(budgetDecreaseMap.keySet());
			budgetNoSet.addAll(budgetIncreaseMap.keySet());

			SqlBuilder whereSql = new SqlBuilder();
			whereSql.append(" dr = 0  ");
			whereSql.append(" and ");
			whereSql.append(" pk_budgetmake in ( select pk_budgetmake from hrpbm_budgetmakedetail where dr = 0  ");
			whereSql.append(" and ");
			whereSql.append("budgetno",budgetNoSet.toArray(new String[0]));
			whereSql.append(" ) ");

			IBudgetMakeVOService budgetMakeVOService = NCLocator.getInstance().lookup(IBudgetMakeVOService.class);
			AggBudgetMakeVO[] aggBudgetMakeVOS = budgetMakeVOService.listAggBudgetMakeVOByCondition(whereSql.toString());
			if(ArrayUtils.isNotEmpty(aggBudgetMakeVOS)) {
				for (AggBudgetMakeVO aggBudgetMakeVO : aggBudgetMakeVOS) {
					BudgetMakeVO headVO = aggBudgetMakeVO.getParentVO();
					BudgetMakeDetailVO[] bodyVOs = (BudgetMakeDetailVO[])aggBudgetMakeVO.getChildren(BudgetMakeDetailVO.class);
					if(ArrayUtils.isEmpty(bodyVOs)) {
						continue;
					}
					for (BudgetMakeDetailVO bodyVO : bodyVOs) {
						String budgetNo = bodyVO.getBudgetno();
						UFDouble finalAmount = bodyVO.getAmount() == null ? UFDouble.ZERO_DBL : bodyVO.getAmount();
						UFDouble increaseAmount = budgetIncreaseMap.get(budgetNo);
						if(increaseAmount != null) {
							UFDouble adjustAmount = bodyVO.getAmount_adjust();
							if(adjustAmount == null){
								adjustAmount = UFDouble.ZERO_DBL;
							}

							bodyVO.setAmount_adjust(adjustAmount.add(increaseAmount));
							bodyVO.setAmount(finalAmount.add(increaseAmount));
							bodyVO.setStatus(VOStatus.UPDATED);
							headVO.setStatus(VOStatus.UPDATED);
						}
						UFDouble decreaseAmount = budgetDecreaseMap.get(budgetNo);
						if(decreaseAmount != null) {
							UFDouble adjustAmount = bodyVO.getAmount_adjust();
							if(adjustAmount == null){
								adjustAmount = UFDouble.ZERO_DBL;
							}
							bodyVO.setAmount_adjust(adjustAmount.sub(decreaseAmount));
							bodyVO.setAmount(finalAmount.sub(decreaseAmount));
							bodyVO.setStatus(VOStatus.UPDATED);
							headVO.setStatus(VOStatus.UPDATED);
						}

					}
				}
				budgetMakeVOService.saveAggBudgetMakeVO(aggBudgetMakeVOS);
			}


		}

		vos = this.saveAggBudgetswapVO(vos);

		/*
		 * ADD BY WW 推送业务控制平台
		 */
		for (AggBudgetswapVO aggBudgetswapVO : vos) {
			BudgetswapVO headVO = aggBudgetswapVO.getParentVO();
			Integer approvestatus = headVO.getApprovestatus();
			if (approvestatus == null || !approvestatus.equals(BillStatusEnum.APPROVED.toIntValue())) {
				continue;
			}

			this.pushBudgetForApproveAndUnApprove(aggBudgetswapVO,"APPROVE");

		}


		// modify by wy at 2024年11月26日 预算调剂回写采购系统四级项目
		BudgetSwapWriteBackForPeojectUtil.ApproveWriteBackForProjectUtil(vos);
//		int c = 1 / 0;
		return vos;

	}


	@Override
	public AggBudgetswapVO[] callbackSAVEBASE(AggBudgetswapVO...vos) throws BusinessException{
		if(ArrayUtils.isEmpty(vos)) {
			return null;
		}

		return vos;

	}


	@Override
	public AggBudgetswapVO[] callbackUNAPPROVE(AggBudgetswapVO...vos) throws BusinessException{
		if(ArrayUtils.isEmpty(vos)) {
			return null;
		}
		vos = this.saveAggBudgetswapVO(vos);
		return vos;

	}

	/**
	 * 保存时 推送预算（校验余额、冻结）
	 *
	 * @param swapVos
	 * @param oriSwapVos
	 * @param isNeedCheck 是否进行校验
	 * @throws BusinessException
	 */
	public void pushBudgetForSave(AggBudgetswapVO[] swapVos, AggBudgetswapVO[] oriSwapVos, boolean isNeedCheck) throws BusinessException {

		Map<String, AggBudgetswapVO> oriSwapVoMap = new HashMap<String,AggBudgetswapVO>();

		if(oriSwapVos != null && oriSwapVos.length > 0) {
			oriSwapVoMap = Arrays.asList(oriSwapVos).stream()
					.collect(Collectors.toMap(AggBudgetswapVO::getPrimaryKey, Function.identity()));
		}

		AggBudgetswapVO tempOriSwapVo = null;
		for(AggBudgetswapVO swapVo : swapVos){

			//获取修改前的VO
			tempOriSwapVo = oriSwapVoMap.get(swapVo.getPrimaryKey());

			BudgetswapVO parentVo = swapVo.getParentVO();

			if (swapVo.getAllChildrenVO() == null || swapVo.getAllChildrenVO().length == 0) {
				swapVo = this.findAggBudgetswapVOByPk(swapVo.getPrimaryKey());
			}

			/**
			 * 保存时主要进行 调减金额的校验及冻结，所以只执行调出即可
			 */
			//先执行调出，调出时需要把金额转换为负数
			AggBudgetswapVO aggVo_out = this.getAggVoForPushToCtrl_out(swapVo);
			AggBudgetswapVO oriVo_out = this.getAggVoForPushToCtrl_out(tempOriSwapVo);
            new BmBudgetControlServiceImpl().pushDataToControl(parentVo.getPk_group(), parentVo.getPk_org(), "BM35",
					null, "调出", "SAVE", aggVo_out, oriVo_out, isNeedCheck);

//			//再执行调入
//			AggBudgetswapVO aggVo_in = (AggBudgetswapVO)swapVo.clone();
//			AggBudgetswapVO oriVo_in = tempOriSwapVo == null ? null : (AggBudgetswapVO)tempOriSwapVo.clone();
//
//			new BmBudgetControlServiceImpl().pushDataToControl(parentVo.getPk_group(), parentVo.getPk_org(), "BM35",
//					null, "调入", "SAVE", aggVo_in, oriVo_in, isNeedCheck);

		}


	}

	/**
	 * 删除时 推送预算（校验余额、取消冻结）
	 *
	 * @param swapVos
	 * @throws BusinessException
	 */
	public void pushBudgetForDelete(AggBudgetswapVO[] swapVos) throws BusinessException {

		for(AggBudgetswapVO swapVo : swapVos){

			BudgetswapVO parentVo = swapVo.getParentVO();

			if (swapVo.getAllChildrenVO() == null || swapVo.getAllChildrenVO().length == 0) {
				throw new BusinessException("预算调剂明细为空，请检查。");
			}

			/**
			 * 保存时主要进行 调减金额的校验及冻结，所以只执行调出即可
			 */
			//执行调出，调出时需要把金额转换为负数
			AggBudgetswapVO aggVo_out = this.getAggVoForPushToCtrl_out(swapVo);
			new BmBudgetControlServiceImpl().pushDataToControl(parentVo.getPk_group(), parentVo.getPk_org(), "BM35",
					null, "调出", "DELETE", aggVo_out, null, true);
		}


	}

	/**
	 * 推送数据到业务控制平台
	 *
	 * @param swapVo
	 * @param action
	 * @throws BusinessException
	 */
	public void pushBudgetForApproveAndUnApprove(AggBudgetswapVO swapVo, String action) throws BusinessException {

		if (swapVo.getChildrenVO() == null || swapVo.getChildrenVO().length == 0) {
			swapVo = findAggBudgetswapVOByPk(swapVo.getPrimaryKey());
		}

		BmBudgetControlServiceImpl bmBudgetControlServiceImplnew = new BmBudgetControlServiceImpl();

		/*
		 * 推送数据到业务控制平台
		 */
		//先执行调出
		AggBudgetswapVO aggVo_out = getAggVoForPushToCtrl_out(swapVo);
		new BmBudgetControlServiceImpl().pushDataToControl(aggVo_out.getParentVO().getPk_group(), aggVo_out.getParentVO().getPk_org(), "BM35",
				null, "调出", action, aggVo_out, null,true);

		//再执行调入
		AggBudgetswapVO aggVo_in = swapVo;
		new BmBudgetControlServiceImpl().pushDataToControl(aggVo_in.getParentVO().getPk_group(), aggVo_in.getParentVO().getPk_org(), "BM35",
				null, "调入", action, aggVo_in, null, true);

	}

	/**
	 * 调出时，将调剂金额转为相反数
	 * @param aggBudgetswapVO
	 * @return
	 * @throws BusinessException
	 */
	private AggBudgetswapVO getAggVoForPushToCtrl_out(AggBudgetswapVO aggBudgetswapVO) throws BusinessException{

		if(aggBudgetswapVO == null)
			return null;

		AggBudgetswapVO aggVo_out = (AggBudgetswapVO)aggBudgetswapVO.clone();

		BudgetswapBVO[] bodyVos = (BudgetswapBVO[])aggVo_out.getChildren(BudgetswapBVO.class);

		for(BudgetswapBVO bodyVo : bodyVos){

			bodyVo.setSwap_amount(bodyVo.getSwap_amount().multiply(-1));

		}

		return aggVo_out;

	}

	@Override
	public BillCodeContext getBillCodeContext(String coderuleid) throws BusinessException{
		return super.getBillCodeContext(coderuleid);
	}

	@Override
	public BillCodeContext getBillCodeContext(String coderuleid, String pkgroup, String pkorg) throws BusinessException{
		return super.getBillCodeContext(coderuleid,pkgroup,pkorg);
	}

}
